package com.dlc.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dlc.shop.bean.app.dto.ShopCartItemDiscountDto;
import com.dlc.shop.bean.app.dto.ShopCartItemDto;
import com.dlc.shop.bean.app.dto.ShopCartOrderDto;
import com.dlc.shop.bean.dto.OrderRefundDto;
import com.dlc.shop.bean.dto.StockBillLogItemDTO;
import com.dlc.shop.bean.dto.StockPointSkuDTO;
import com.dlc.shop.bean.enums.*;
import com.dlc.shop.bean.event.EsProductUpdateEvent;
import com.dlc.shop.bean.model.*;
import com.dlc.shop.bean.param.ProductParam;
import com.dlc.shop.bean.param.StockBillExportParam;
import com.dlc.shop.bean.vo.SkuStockVO;
import com.dlc.shop.bean.vo.StockPointSkuVO;
import com.dlc.shop.bean.vo.WarehouseVO;
import com.dlc.shop.common.config.Constant;
import com.dlc.shop.common.constants.LuaOperateEnum;
import com.dlc.shop.common.constants.SegmentIdKey;
import com.dlc.shop.common.constants.StockPointType;
import com.dlc.shop.common.enums.SysTypeEnum;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.i18n.I18nMessage;
import com.dlc.shop.common.util.*;
import com.dlc.shop.dao.StationMapper;
import com.dlc.shop.dao.StockBillLogItemMapper;
import com.dlc.shop.dao.StockBillLogMapper;
import com.dlc.shop.manager.impl.LangManager;
import com.dlc.shop.manager.impl.StockManager;
import com.dlc.shop.service.*;
import com.dlc.shop.util.StockUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.Sheet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 出入库明细
 *
 * @author LGH
 * @date 2021-09-09 13:11:15
 */
@Service
@RequiredArgsConstructor
public class StockBillLogServiceImpl extends ServiceImpl<StockBillLogMapper, StockBillLog> implements StockBillLogService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final StockBillLogMapper stockBillLogMapper;
    private final StockBillLogItemMapper stockBillLogItemMapper;
    private final ProductService productService;
    private final SkuService skuService;
    private final SegmentService segmentService;
    private final LangManager langManager;
    private final StockBillLogItemService stockBillLogItemService;
    private final WarehouseService warehouseService;
    private final SkuStockService skuStockService;
    private final StockPointSkuService stockPointSkuService;
    private final StationMapper stationMapper;
    private final StockManager stockManager;
    private final ApplicationEventPublisher eventPublisher;

    // 最大合并列
    private final int[] rowAccessWindowSize = {0};
    /**
     * 导出出库明细excel列
     */
    private final static String[] OUT_EXPORT_COL = {
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.outboundNo"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.stockPointName"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.stockPointType"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.sourceOderNo"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.billType"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.reason"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.billStatus"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.outboundTime"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.marker"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.remark"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.createTime"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.prodName"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.prodCode"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.prodSpec"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.outboundCount"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.singlePrice"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.amount")
    };

    /**
     * 导出入库明细excel列
     */
    private final static String[] IN_EXPORT_COL = {
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.inboundNo"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.stockPointName"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.stockPointType"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.sourceOderNo"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.billType"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.reason"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.billStatus"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.inboundTime"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.marker"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.remark"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.createTime"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.prodName"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.prodCode"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.prodSpec"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.inboundCount"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.singlePrice"),
            I18nMessage.getMessage("yami.stock.bill.log.excel.header.amount")
    };

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addStockBillLog(StockBillLog stockBillLog) {
        // 校验部分信息
        this.checkInfo(stockBillLog);
        Date now = new Date();
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setStockBillNo(this.getStockBillNoByType(null, StockType.instance(stockBillLog.getType())));
        // 保存出入货明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存出入货商品信息项
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLog.getStockBillLogItems());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveInfo(StockBillLog stockBillLog) {
        Date now = new Date();
        // 校验部分信息
        this.checkInfo(stockBillLog);

        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);

        stockBillLog.setStockBillNo(this.getStockBillNoByType(null, StockType.instance(stockBillLog.getType())));
        // 处理金额并对商品完成出入库操作
        this.processingAmountAndStock(stockBillLog);
        // 保存出入货明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存出入货商品信息项
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLog.getStockBillLogItems());
        // 更新商品信息
        List<Long> prodIds = stockBillLog.getStockBillLogItems().stream().map(StockBillLogItem::getProdId).collect(Collectors.toList());
        eventPublisher.publishEvent(new EsProductUpdateEvent(null, prodIds, EsOperationType.UPDATE_BATCH));
    }

    @Override
    public IPage<StockBillLog> pageByParams(PageParam<StockBillLog> page, StockBillLog stockBillLog) {
        IPage<StockBillLog> stockBillLogPage = stockBillLogMapper.pageByParams(page, stockBillLog);
        langManager.getStockBillLogLang(stockBillLogPage.getRecords());
        return stockBillLogPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfo(StockBillLog stockBillLog) {
        Date now = new Date();
        this.checkInfo(stockBillLog);
        stockBillLog.setUpdateTime(now);
        this.processingAmountAndStock(stockBillLog);

        // 更新出入库明细信息
        stockBillLogMapper.updateById(stockBillLog);
        // 更新出入货商品信息项
        stockBillLogItemMapper.delete(Wrappers.lambdaQuery(StockBillLogItem.class).eq(StockBillLogItem::getStockBillLogId, stockBillLog.getStockBillLogId()));
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLog.getStockBillLogItems());
        // 更新商品信息
        List<Long> prodIds = stockBillLog.getStockBillLogItems().stream().map(StockBillLogItem::getProdId).collect(Collectors.toList());
        eventPublisher.publishEvent(new EsProductUpdateEvent(null, prodIds, EsOperationType.UPDATE_BATCH));
    }

    @Override
    public void exportStockBillLog(StockBillLog stockBillLog, HttpServletResponse response) {
        List<StockBillExportParam> stockBillExportParamList = stockBillLogMapper.listExportStockBillLog(stockBillLog);
        // 补充库存点信息
        List<WarehouseVO> warehouseVOList = warehouseService.listWarehouseByShopId(stockBillLog.getShopId(), SysTypeEnum.MULTISHOP.value());
        Map<Long, String> warehouseNameMap = warehouseVOList.stream().collect(Collectors.toMap(WarehouseVO::getWarehouseId, WarehouseVO::getWarehouseName));
        List<Station> stationList = stationMapper.selectList(new LambdaQueryWrapper<>(Station.class).eq(Station::getShopId, stockBillLog.getShopId()));
        Map<Long, String> stationNameMap = stationList.stream().collect(Collectors.toMap(Station::getStationId, Station::getStationName));
        stockBillExportParamList.forEach(s -> {
            if (Objects.equals(s.getStockPointType(), StockPointType.WAREHOUSE.getValue())) {
                s.setStockPointName(warehouseNameMap.getOrDefault(s.getStockPointId(), "_"));
            }
            if (Objects.equals(s.getStockPointType(), StockPointType.STATION.getValue())) {
                s.setStockPointName(stationNameMap.getOrDefault(s.getStockPointId(), "_"));
            }
        });
        langManager.getStockBillExportParamLang(stockBillExportParamList);
        for (StockBillExportParam stockBillExportParam : stockBillExportParamList) {
            if (StrUtil.isNotBlank(stockBillExportParam.getEmployeeMobile())) {
                stockBillExportParam.setEmployeeMobile(PhoneUtil.hideBetween(stockBillExportParam.getEmployeeMobile()).toString());
            }
        }
        if (CollUtil.isEmpty(stockBillExportParamList)) {
            return;
        }
        Map<String, List<StockBillExportParam>> listMap = stockBillExportParamList.stream().collect(Collectors.groupingBy(StockBillExportParam::getStockBillNo));
        listMap.forEach((k, v) -> {
            if (Objects.nonNull(v) && v.size() > rowAccessWindowSize[0]) {
                rowAccessWindowSize[0] = v.size();
            }
        });
        ExcelWriter writer = ExcelUtil.getBigWriter(++rowAccessWindowSize[0]);
        final List<String> exportCol = Arrays.asList(
                Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? OUT_EXPORT_COL : IN_EXPORT_COL
        );
        // 最大列数量
        int maxColNum = exportCol.size();

        Sheet sheet = writer.getSheet();
        // 设置列宽
        this.setColumnWidth(sheet);
        // 标题
        writer.merge(maxColNum - 1, Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value())
                ? I18nMessage.getMessage("yami.stock.bill.log.excel.title.outbound") : I18nMessage.getMessage("yami.stock.bill.log.excel.title.inbound")
        );
        // 列名
        writer.writeRow(exportCol);

        Map<String, List<StockBillExportParam>> stockBillMap = new LinkedHashMap<>();
        // 用map把出入库单号相同的项目合并
        for (StockBillExportParam stockBillExportParam : stockBillExportParamList) {
            List<StockBillExportParam> paramList = stockBillMap.getOrDefault(stockBillExportParam.getStockBillNo(), new ArrayList<StockBillExportParam>());
            paramList.add(stockBillExportParam);
            stockBillMap.put(stockBillExportParam.getStockBillNo(), paramList);
        }
        // 处理数据
        handleExcel(stockBillLog, writer, stockBillMap);
        PoiExcelUtil.writeExcel(response, writer);
    }

    private void handleExcel(StockBillLog stockBillLog, ExcelWriter writer, Map<String, List<StockBillExportParam>> stockBillMap) {
        int row = 1;
        for (Map.Entry<String, List<StockBillExportParam>> entry : stockBillMap.entrySet()) {

            final String stockBillNo = entry.getKey();
            final List<StockBillExportParam> paramList = entry.getValue();
            // 取出任意一个stockBillExportParam（因为paramList中的实例都有一部分公共属性）
            final StockBillExportParam stockBillExportParam = paramList.get(0);
            int firstRow = row + 1;
            int lastRow = row + paramList.size();
            int col = -1;

            // 出入库单号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getStockBillNo());
            // 库存点名称
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getStockPointName());
            // 库存点类型
            StockPointType stockPointTypeName = Objects.isNull(stockBillExportParam.getStockPointType()) ? null : StockPointType.instance(stockBillExportParam.getStockPointType());
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, Objects.isNull(stockPointTypeName) ? "" : I18nMessage.getMessage(stockPointTypeName.getI18nTag()));
            // 关联订单号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getSourceOrderNo());
            // 单据类型
            StockBillType stockBillType = StockBillType.instance(stockBillExportParam.getStockBillType());
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, Objects.isNull(stockBillType) ? "" : I18nMessage.getMessage(stockBillType.getI18nTag()));

            // 原因
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getReason());
            // 状态
            StockBillStatus stockBillStatus = StockBillStatus.instance(stockBillExportParam.getStatus());
            String statusStr;
            if (Objects.isNull(stockBillStatus)) {
                statusStr = "";
            } else {
                statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                        (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? I18nMessage.getMessage("yami.stock.bill.log.type.outbound") : I18nMessage.getMessage("yami.stock.bill.log.type.inbound")) :
                        I18nMessage.getMessage(stockBillStatus.getI18nTag());
            }
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, statusStr);

            // 出入库时间
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getBusinessTime());
            // 制单人手机号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getEmployeeMobile());
            // 备注
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getRemark());
            // 创建时间
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getCreateTime());

            for (StockBillExportParam billExportParam : paramList) {
                row++;
                int partCol = col;
                // 商品名称
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getProdName());
                // 商品编码
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getPartyCode());
                // sku名称
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getSkuName());
                // 实际出入库数量
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getStockCount());
                // 成本单价
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getUnitPrice());
                // 成本金额
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, Arith.mul(stockBillExportParam.getStockCount(), stockBillExportParam.getUnitPrice()));
            }
        }
    }

    /**
     * @author chiley
     * @create 2022/5/17 17:34
     */

    private void writeMethod2(StockBillLog stockBillLog, List<StockBillExportParam> stockBillExportParamList, ExcelWriter writer, int rowIndex) {
        int i;
        int j = 0;
        int col = 0;
        for (i = 0; i < stockBillExportParamList.size() - 1; i++, rowIndex++) {
            col = -1;

            j = i;
            while (j < stockBillExportParamList.size() - 1 && Objects.equals(stockBillExportParamList.get(j).getStockBillNo(), stockBillExportParamList.get(j + 1).getStockBillNo())) {
                j++;
                rowIndex++;
            }
            // 出入库单号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getStockBillNo());
            // 关联订单号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getSourceOrderNo());
            // 单据类型
            StockBillType stockBillType = StockBillType.instance(stockBillExportParamList.get(i).getStockBillType());
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, Objects.isNull(stockBillType) ? "" : I18nMessage.getMessage(stockBillType.getI18nTag()));
            // 原因
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getReason());
            // 状态
            StockBillStatus stockBillStatus = StockBillStatus.instance(stockBillExportParamList.get(i).getStatus());
            String statusStr;
            if (Objects.isNull(stockBillStatus)) {
                statusStr = "";
            } else {
                statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                        (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? I18nMessage.getMessage("yami.stock.bill.status.outStock") : I18nMessage.getMessage("yami.stock.bill.status.inStock")) :
                        I18nMessage.getMessage(stockBillStatus.getI18nTag());
            }
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, statusStr);

            // 出入库时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getBusinessTime());
            // 制单人手机号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getEmployeeMobile());

            // 备注
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getRemark());
            // 创建时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getCreateTime());

            i = j;

        }
        handleMethod2(i, j, col, rowIndex, stockBillLog, writer, stockBillExportParamList);
    }

    private void handleMethod2(int i, int j, int col, int rowIndex, StockBillLog stockBillLog, ExcelWriter writer, List<StockBillExportParam> stockBillExportParamList) {
        // 检查最后一行有没有写
        if (j < stockBillExportParamList.size() - 1) {

            int acol = -1;
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getStockBillNo());
            // 关联订单号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getSourceOrderNo());
            // 单据类型
            StockBillType stockBillType = StockBillType.instance(stockBillExportParamList.get(i).getStockBillType());
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, Objects.isNull(stockBillType) ? "" : I18nMessage.getMessage(stockBillType.getI18nTag()));
            // 原因
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getReason());
            // 状态
            StockBillStatus stockBillStatus = StockBillStatus.instance(stockBillExportParamList.get(i).getStatus());
            String statusStr;
            if (Objects.isNull(stockBillStatus)) {
                statusStr = "";
            } else {
                statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                        (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? I18nMessage.getMessage("yami.stock.bill.status.outStock") : I18nMessage.getMessage("yami.stock.bill.status.inStock")) :
                        I18nMessage.getMessage(stockBillStatus.getI18nTag());
            }
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, statusStr);
            // 出入库时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getBusinessTime());
            // 制单人手机号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getEmployeeMobile());
            // 备注
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getRemark());
            // 创建时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getCreateTime());
        }

        // 逐行写入不重复的内容
        rowIndex = 2;
        int temp = col;
        for (StockBillExportParam stockBillExportParam : stockBillExportParamList) {
            col = temp;
            // 商品名称
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getProdName());
            // 商品编码
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getPartyCode());
            // sku名称
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getSkuName());
            // 实际出入库数量
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getStockCount());
            // 成本单价
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getUnitPrice());
            // 成本金额
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, Arith.mul(stockBillExportParam.getStockCount(), stockBillExportParam.getUnitPrice()));
            ++rowIndex;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initialize(List<StockBillLogItemDTO> stockBillLogItemDTOList, Integer stockBillType, Integer type) {
        if (CollUtil.isEmpty(stockBillLogItemDTOList)) {
            return;
        }
        Map<Long, List<StockBillLogItemDTO>> stockBillLogListMap = stockBillLogItemDTOList.stream().collect(Collectors.groupingBy(StockBillLogItemDTO::getStockPointId));
        Date now = new Date();
        List<StockBillLog> stockBillLogList = new ArrayList<>(stockBillLogListMap.size());
        for (Map.Entry<Long, List<StockBillLogItemDTO>> stockBillLogMap : stockBillLogListMap.entrySet()) {
            StockBillLog stockBillLog = new StockBillLog();
            stockBillLog.setCreateTime(now);
            stockBillLog.setUpdateTime(now);
            stockBillLog.setStockBillType(stockBillType);
            stockBillLog.setType(type);
            stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
            stockBillLog.setStockBillNo(this.getStockBillNoByType(null, StockType.instance(StockType.WAREHOUSING.value())));
            stockBillLog.setBusinessTime(now);
            stockBillLog.setStockPointId(stockBillLogMap.getKey());
            stockBillLog.setShopId(stockBillLogMap.getValue().get(0).getShopId());
            stockBillLog.setStockPointType(stockBillLogMap.getValue().get(0).getStockPointType());
            stockBillLog.setEmployeeId(stockBillLogMap.getValue().get(0).getEmployeeId());
            int stock = 0;
            Double amount = 0.0;
            for (StockBillLogItemDTO stockBillLogItemDTO : stockBillLogMap.getValue()) {
                Integer stockCount = Objects.isNull(stockBillLogItemDTO.getStockCount()) ? 0 : stockBillLogItemDTO.getStockCount();
                double unitPrice = Objects.isNull(stockBillLogItemDTO.getUnitPrice()) ? 0.0 : stockBillLogItemDTO.getUnitPrice();
                stock += stockCount;
                amount += Arith.mul(stockCount, unitPrice);
            }
            stockBillLog.setTotalCount(stock);
            stockBillLog.setTotalAmount(amount);
            stockBillLogList.add(stockBillLog);
        }
        // 保存明细信息
        saveBatch(stockBillLogList);
        Map<Long, Long> stockPointLogIdMap = stockBillLogList.stream().collect(Collectors.toMap(StockBillLog::getStockPointId, StockBillLog::getStockBillLogId));
        stockBillLogItemDTOList.forEach(s -> s.setStockBillLogId(stockPointLogIdMap.get(s.getStockPointId())));
        // 保存商品项信息
        stockBillLogItemService.saveBatch(BeanUtil.mapAsList(stockBillLogItemDTOList, StockBillLogItem.class));
    }

    @Override
    public IPage<StockBillLogItem> inquire(PageParam<StockBillLog> page, ProductParam productParam) {
        return skuService.inquireStock(page, productParam);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateStockLogOfOrderList(List<Order> orders, Integer stockBillType, Integer stockType) {
        if (CollUtil.isEmpty(orders)) {
            return;
        }
        Date now = new Date();
        for (Order order : orders) {
            List<OrderItem> list = order.getOrderItems().stream().filter(item -> !Objects.equals(item.getMold(), ProdMoldEnum.COMBO.value())).toList();
            if (list == null || list.size() == 0) {
                logger.info("数据有问题，跳过不进行出入库操作。{}",order.toString());
                continue;
            }
            OrderItem orderItem = list.get(0);
            StockPointType stockPointType = Objects.equals(order.getDvyType(), DeliveryType.STATION.getValue()) ? StockPointType.STATION : StockPointType.WAREHOUSE;
            StockBillLog stockBillLog = this.getStockBillLog(
                    order.getShopId(),
                    null,
                    now,
                    stockBillType,
                    stockType,
                    this.getStockBillNoByType(null, StockType.instance(stockType)),
                    StockBillStatus.SUCCESS.value(),
                    order.getOrderNumber(),
                    stockPointType.getValue(),
                    orderItem.getStockPointId()
            );
            this.generateStockLogOfOrderItemList(order.getOrderItems(), stockBillLog, false);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateStockLogOfShopCartOrderList(List<ShopCartOrderDto> shopCartOrders, Map<String, SkuStockVO> orderResultMap, Integer stockBillType, Integer stockType) {
        if (CollUtil.isEmpty(shopCartOrders)) {
            return;
        }
        Date now = new Date();
        for (ShopCartOrderDto order : shopCartOrders) {
            ShopCartItemDto shopCartItemDto = order.getShopCartItemDiscounts().get(0).getShopCartItems().get(0);
            StockBillLog stockBillLog = this.getStockBillLog(
                    order.getShopId(),
                    null,
                    now,
                    stockBillType,
                    stockType,
                    this.getStockBillNoByType(null, StockType.instance(stockType)),
                    StockBillStatus.SUCCESS.value(),
                    order.getOrderNumber(),
                    shopCartItemDto.getStockPointType(),
                    shopCartItemDto.getStockPointId()
            );
            Map<Long, OrderItem> orderItemMap = new HashMap<>();
            for (ShopCartItemDiscountDto shopCartItemDiscount : order.getShopCartItemDiscounts()) {
                for (ShopCartItemDto shopCartItem : shopCartItemDiscount.getShopCartItems()) {
                    if (Objects.equals(shopCartItem.getMold(), ProdMoldEnum.COMBO.value())) {
                        for (ShopCartItemDto comboShopCartItem : shopCartItem.getComboShopCartItems()) {
                            loadOrderItem(comboShopCartItem, orderItemMap);
                            stockBillLog.setStockPointId(comboShopCartItem.getStockPointId());
                            stockBillLog.setStockPointType(comboShopCartItem.getStockPointType());
                        }
                    } else {
                        loadOrderItem(shopCartItem, orderItemMap);
                    }
                    if (CollUtil.isNotEmpty(shopCartItem.getGiveawayShopCartItemList())) {
                        for (ShopCartItemDto giveawayShopCartItem : shopCartItem.getGiveawayShopCartItemList()) {
                            loadOrderItem(giveawayShopCartItem, orderItemMap);
                        }
                    }
                }
            }
            this.generateStockLogOfOrderItemList(new ArrayList<>(orderItemMap.values()), stockBillLog, false);
        }
    }

    private static void loadOrderItem(ShopCartItemDto shopCartItem, Map<Long, OrderItem> orderItemMap) {
        OrderItem orderItem = orderItemMap.get(shopCartItem.getSkuId());
        if (Objects.isNull(orderItem)) {
            orderItem = new OrderItem();
            orderItem.setProdId(shopCartItem.getProdId());
            orderItem.setSkuId(shopCartItem.getSkuId());
            orderItem.setStatus(-1);
            orderItem.setProdCount(0);
            orderItem.setStockPointId(shopCartItem.getStockPointId());
            orderItem.setStockPointType(shopCartItem.getStockPointType());
            orderItem.setAfterStock(shopCartItem.getAfterStock());
            orderItemMap.put(shopCartItem.getSkuId(), orderItem);
        }
        orderItem.setProdCount(orderItem.getProdCount() + shopCartItem.getProdCount());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateStockLogOfRefundOrder(OrderRefundDto orderRefundDto, Integer stockBillType, Integer stockType) {
        Date now = new Date();
        StockBillLog stockBillLog = this.getStockBillLog(
                orderRefundDto.getShopId(),
                null,
                now,
                stockBillType,
                stockType,
                this.getStockBillNoByType(null, StockType.instance(stockType)),
                StockBillStatus.SUCCESS.value(),
                orderRefundDto.getOrderNumber(),
                orderRefundDto.getStockPointType(),
                orderRefundDto.getStockPointId()
        );
        this.generateStockLogOfOrderItemList(orderRefundDto.getOrderItems(), stockBillLog, true);
    }

    private StockBillLog getStockBillLog(Long shopId, Long employeeId, Date businessTime, Integer stockBillLogType, Integer stockType,
                                         String stockBillNo, Integer status, String sourceOrderNo, Integer stockPointType, Long stockPointId) {
        Date now = new Date();
        StockBillLog stockBillLog = new StockBillLog();
        stockBillLog.setShopId(shopId);
        stockBillLog.setEmployeeId(employeeId);
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setBusinessTime(businessTime);
        stockBillLog.setStockBillType(stockBillLogType);
        stockBillLog.setType(stockType);
        stockBillLog.setStockBillNo(stockBillNo);
        stockBillLog.setStatus(status);
        stockBillLog.setSourceOrderNo(sourceOrderNo);
        stockBillLog.setStockPointType(stockPointType);
        stockBillLog.setStockPointId(stockPointId);
        return stockBillLog;
    }

    private StockBillLogItem getStockBillLogItem(Long skuId, Long prodId, Double unitPrice, Integer stockCount, Integer afterStock) {
        StockBillLogItem stockBillLogItem = new StockBillLogItem();
        stockBillLogItem.setSkuId(skuId);
        stockBillLogItem.setProdId(prodId);
        stockBillLogItem.setUnitPrice(unitPrice);
        stockBillLogItem.setStockCount(stockCount);
        stockBillLogItem.setAfterStock(afterStock);
        return stockBillLogItem;
    }

    /**
     * 根据订单项完成库存明细信息的生成和保存
     *
     * @param orderItems
     * @param stockBillLog
     * @param isRefundOrder
     */
    private void generateStockLogOfOrderItemList(List<OrderItem> orderItems, StockBillLog stockBillLog, Boolean isRefundOrder) {
        List<StockBillLogItem> stockBillLogItemList = new ArrayList<>();
        double totalAmount = 0.0;
        Integer totalCount = 0;
        List<OrderItem> billItemList = new ArrayList<>(orderItems);
        for (OrderItem orderItem : orderItems) {
            if (CollUtil.isNotEmpty(orderItem.getComboList())) {
                billItemList.addAll(orderItem.getComboList());
            }
        }
        for (OrderItem orderItem : billItemList) {
            if (isRefundOrder && orderItem.getStatus() == 0) {
                continue;
            }
            // 组合商品过滤
            if (Objects.equals(orderItem.getMold(), ProdMoldEnum.COMBO.value())) {
                continue;
            }
            // 普通商品sku
            Sku sku = skuService.getOne(Wrappers.lambdaQuery(Sku.class).eq(Sku::getSkuId, orderItem.getSkuId()));
            // 删除的sku不再添加记录
            if (Objects.equals(sku.getIsDelete(), 1)) {
                continue;
            }
            StockBillLogItem stockBillLogItem = this.getStockBillLogItem(
                    orderItem.getSkuId(),
                    orderItem.getProdId(),
                    sku.getPrice(),
                    isRefundOrder ? (orderItem.getStatus() == -1 ? orderItem.getProdCount() : orderItem.getStatus()) : orderItem.getProdCount(),
                    sku.getStocks()
            );
            // 商品可售卖库存剩余量
            stockBillLogItem.setAfterStock(orderItem.getAfterStock());
            totalAmount = Arith.add(totalAmount, Arith.mul(isRefundOrder ? (orderItem.getStatus() == -1 ? orderItem.getProdCount() : orderItem.getStatus()) : orderItem.getProdCount(), sku.getPrice()));
            totalCount += isRefundOrder ? (orderItem.getStatus() == -1 ? orderItem.getProdCount() : orderItem.getStatus()) : orderItem.getProdCount();
            stockBillLogItemList.add(stockBillLogItem);
        }
        if (totalCount == 0) {
            return;
        }
        OrderItem orderItem = billItemList.stream().filter(item -> !Objects.equals(item.getMold(), ProdMoldEnum.COMBO.value())).toList().get(0);
        stockBillLog.setTotalAmount(totalAmount);
        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setStockPointId(stockBillLog.getStockPointId() == null ? orderItem.getStockPointId() : stockBillLog.getStockPointId());
        stockBillLog.setStockPointType(stockBillLog.getStockPointType() == null ? orderItem.getStockPointType() : stockBillLog.getStockPointType());
        // 保存初始化明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存初始化商品项信息
        if (CollUtil.isEmpty(stockBillLogItemList)) {
            return;
        }
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLogItemList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void voided(Long shopId, Long stockBillLogId) {
        StockBillLog stockBillLog = stockBillLogMapper.selectById(stockBillLogId);
        if (Objects.isNull(stockBillLog) || !Objects.equals(stockBillLog.getStatus(), StockBillStatus.AWAIT_SUBMIT.value())) {
            throw new YamiShopBindException("当前数据不存在或状态已经发生改变，请刷新后重试");
        }
        if (!Objects.equals(shopId, stockBillLog.getShopId()) && !Objects.equals(shopId, Constant.PLATFORM_SHOP_ID)) {
            throw new YamiShopBindException("当前数据不属于你的店铺");
        }
        boolean isSuccess = this.update(Wrappers.lambdaUpdate(StockBillLog.class)
                .set(StockBillLog::getStatus, StockBillStatus.ABOLISHED.value())
                .eq(StockBillLog::getStockBillLogId, stockBillLogId)
                .eq(StockBillLog::getStatus, stockBillLog.getStatus())
        );
        if (!isSuccess) {
            throw new YamiShopBindException("操作失败，请刷新后重试");
        }
    }

    @Override
    public StockBillLog getByStockBillLogId(Long stockBillLogId) {
        StockBillLog stockBillLog = stockBillLogMapper.getByStockBillLogId(stockBillLogId);
        langManager.getStockBillLogLang(stockBillLog);
        return stockBillLog;
    }

    @Override
    public IPage<StockBillLog> purchasePage(PageParam<StockBillLog> page, StockBillLog stockBillLog) {
        IPage<StockBillLog> stockBillLogPage = stockBillLogMapper.pageByParams(page, stockBillLog);
        langManager.getStockBillLogLang(stockBillLogPage.getRecords());
        return stockBillLogPage;
    }

    @Override
    public StockBillLog purchaseInfo(Long stockBillLogId) {
        StockBillLog stockBillLog = stockBillLogMapper.info(stockBillLogId);
        if (Objects.isNull(stockBillLog)) {
            throw new YamiShopBindException("yami.data.deleted.or.not.exist");
        }
        if (CollUtil.isNotEmpty(stockBillLog.getStockBillLogItems())) {
            loadProdAndSkuInfo(stockBillLog.getStockBillLogItems(), stockBillLog.getStockPointId());
        }
        // 补充仓库信息
        if (!Objects.isNull(stockBillLog.getStockPointId())) {
            WarehouseVO warehouseVO = warehouseService.getByWarehouseId(stockBillLog.getStockPointId());
            if (!Objects.isNull(warehouseVO)) {
                stockBillLog.setStockPointName(warehouseVO.getWarehouseName());
            }
        }
        return stockBillLog;
    }

    @Override
    public void generateStockLogOfEditInfo(StockBillLog stockBillLog, List<Sku> skuList) {
        Date now = new Date();
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setBusinessTime(now);
        stockBillLog.setStockBillNo((Objects.equals(stockBillLog.getStockBillType(), StockBillType.EDIT_OUTBOUND.value()) ? "OB" : "IB") + segmentService.getSegmentId(SegmentIdKey.STOCK));
        stockBillLog.setType(Objects.equals(stockBillLog.getStockBillType(), StockBillType.EDIT_OUTBOUND.value()) ? StockType.OUT_OF_STOCK.value() : StockType.WAREHOUSING.value());
        stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
        List<StockBillLogItem> stockBillLogItemList = new ArrayList<>();
        double totalAmount = 0.0;
        int totalCount = 0;
        if (CollUtil.isNotEmpty(skuList)) {
            for (Sku sku : skuList) {
                int changeStock = sku.getChangeStock() < 0 ? -sku.getChangeStock() : sku.getChangeStock();
                StockBillLogItem stockBillLogItem = this.getStockBillLogItem(
                        sku.getSkuId(),
                        sku.getProdId(),
                        sku.getPrice(),
                        changeStock,
                        sku.getStocks()
                );
                totalCount += changeStock;
                totalAmount = Arith.add(totalAmount, Arith.mul(sku.getPrice(), changeStock));
                stockBillLogItemList.add(stockBillLogItem);
            }
        }
        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setTotalAmount(totalAmount);
        // 保存初始化明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存初始化商品项信息
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLogItemList);
    }

    @Override
    public void generateStockLogOfTakeStock(StockBillLog stockBillLog, List<Sku> skuList) {
        if (CollUtil.isEmpty(skuList)) {
            return;
        }
        Date now = new Date();
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setBusinessTime(now);
        stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
        stockBillLog.setStockBillNo(this.getStockBillNoByType(StockBillType.instance(stockBillLog.getStockBillType()), null));
        stockBillLog.setType(Objects.equals(stockBillLog.getStockBillType(), StockBillType.LOSS_OUTBOUND.value()) ? StockType.OUT_OF_STOCK.value() : StockType.WAREHOUSING.value());
        List<StockBillLogItem> stockBillLogItemList = new ArrayList<>();
        double totalAmount = 0.0;
        int totalCount = 0;
        if (CollUtil.isNotEmpty(skuList)) {
            for (Sku sku : skuList) {
                int changeStock = sku.getChangeStock() < 0 ? -sku.getChangeStock() : sku.getChangeStock();
                StockBillLogItem stockBillLogItem = this.getStockBillLogItem(
                        sku.getSkuId(),
                        sku.getProdId(),
                        sku.getPrice(),
                        changeStock,
                        sku.getStocks()
                );
                totalCount += changeStock;
                totalAmount = Arith.add(totalAmount, Arith.mul(sku.getPrice(), changeStock));
                stockBillLogItemList.add(stockBillLogItem);
            }
        }

        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setTotalAmount(totalAmount);
        // 保存初始化明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存初始化商品项信息
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLogItemList);
    }

    @Override
    public void removeEmptyLog() {
        stockBillLogMapper.removeEmptyLog();
    }

    private void loadProdAndSkuInfo(List<StockBillLogItem> stockBillLogItems,
                                    Long stockPointId) {
        List<Long> prodIds = new ArrayList<>();
        List<Long> skuIds = new ArrayList<>();
        for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
            prodIds.add(stockBillLogItem.getProdId());
            skuIds.add(stockBillLogItem.getSkuId());
        }
        // 查询库存
        List<SkuStockVO> skuStocks = new ArrayList<>(skuIds.size());
        for (Long skuId : skuIds) {
            skuStocks.add(new SkuStockVO(skuId, stockPointId));
        }
        Map<String, SkuStockVO> skuStockMap = skuStockService.listSkuStock(skuStocks);
        // 普通商品信息
        Map<Long, Product> prodMap;
        if (CollUtil.isNotEmpty(prodIds) && CollUtil.isNotEmpty(skuIds)) {
            List<Product> products = productService.prodAndSkuInfo(prodIds, skuIds);
            prodMap = products.stream().collect(Collectors.toMap(Product::getProdId, p -> p));
        } else {
            prodMap = new HashMap<>(0);
        }
        SkuStockVO skuStockVO;
        for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
            if (!prodMap.containsKey(stockBillLogItem.getProdId())) {
                continue;
            }
            Product product = prodMap.get(stockBillLogItem.getProdId());
            Sku sku = null;
            for (Sku skuDb : product.getSkuList()) {
                if (Objects.equals(skuDb.getSkuId(), stockBillLogItem.getSkuId())) {
                    sku = skuDb;
                    break;
                }
            }
            if (Objects.isNull(sku)) {
                return;
            }
            stockBillLogItem.setPic(product.getPic());
            stockBillLogItem.setProdName(product.getProdName());
            stockBillLogItem.setSkuName(sku.getSkuName());
            stockBillLogItem.setPartyCode(sku.getPartyCode());
            stockBillLogItem.setIsDelete(sku.getIsDelete());
            skuStockVO = skuStockMap.get(StockUtil.skuPointKey(sku.getSkuId(), stockPointId));
            stockBillLogItem.setStocks(Objects.isNull(skuStockVO) ? 0 : skuStockVO.getStock());
        }
    }

    private void setColumnWidth(Sheet sheet) {
        // 出入库单号
        sheet.setColumnWidth(0, 40 * 256);
        // 关联订单号
        sheet.setColumnWidth(1, 40 * 256);
        // 单据类型
        sheet.setColumnWidth(2, 20 * 256);
        // 状态
        sheet.setColumnWidth(3, 20 * 256);
        // 出入库时间
        sheet.setColumnWidth(4, 20 * 256);
        // 制单人手机号
        sheet.setColumnWidth(5, 20 * 256);
        // 备注
        sheet.setColumnWidth(6, 60 * 256);
        // 创建时间
        sheet.setColumnWidth(7, 20 * 256);
        // 商品名称
        sheet.setColumnWidth(8, 40 * 256);
        // 商品编码
        sheet.setColumnWidth(9, 40 * 256);
        // sku名称
        sheet.setColumnWidth(10, 30 * 256);
        // 实际出入库数量
        sheet.setColumnWidth(11, 20 * 256);
        // 成本单价
        sheet.setColumnWidth(12, 20 * 256);
        // 成本金额
        sheet.setColumnWidth(13, 20 * 256);
        // 商品备注
        sheet.setColumnWidth(14, 60 * 256);
    }

    /**
     * 校验出入库明细信息
     *
     * @param stockBillLog
     */
    private void checkInfo(StockBillLog stockBillLog) {
        if (CollUtil.isEmpty(stockBillLog.getStockBillLogItems())) {
            // 出入库商品项不能为空
            throw new YamiShopBindException("商品项不能为空");
        }
        if (Objects.nonNull(stockBillLog.getStockBillLogId())) {
            // 更新时校验
            StockBillLog dbStockBillLog = stockBillLogMapper.selectById(stockBillLog.getStockBillLogId());
            if (!Objects.equals(stockBillLog.getShopId(), dbStockBillLog.getShopId())) {
                throw new YamiShopBindException("当前出入库信息不属于你的店铺");
            }
            if (!Objects.equals(dbStockBillLog.getStatus(), StockBillStatus.AWAIT_SUBMIT.value())) {
                throw new YamiShopBindException("当前信息已完成或已作废，无法继续修改，请刷新页面");
            }
            stockBillLog.setStockBillNo(dbStockBillLog.getStockBillNo());
        }
    }

    private void processingAmountAndStock(StockBillLog stockBillLog) {
        List<StockBillLogItem> stockBillLogItems = stockBillLog.getStockBillLogItems();
        Sku sku = null;
        boolean flag = false;
        Integer totalCount = 0;
        double totalAmount = 0.0;
        Map<String, StockPointSkuVO> stockPointSkuMap = new HashMap<>(stockBillLogItems.size());
        List<SkuStockVO> changeStockList = new ArrayList<>(stockBillLog.getStockBillLogItems().size());
        for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
            String key = StockUtil.skuPointKey(stockBillLogItem.getSkuId(), stockBillLogItem.getStockPointId());
            if (stockPointSkuMap.containsKey(key)) {
                continue;
            }
            stockPointSkuMap.put(key, new StockPointSkuVO(stockBillLogItem.getSkuId(), stockBillLogItem.getStockPointId()));
        }
        List<StockPointSkuVO> stockPointSkuVOS = stockPointSkuService.listByPointId(stockBillLog.getStockPointId());
        Map<Long, StockPointSkuVO> stockPointSkuVOMap = stockPointSkuVOS.stream().collect(Collectors.toMap(StockPointSkuVO::getSkuId, s -> s));
        List<StockPointSkuDTO> addPointSkus = new ArrayList<>();
        if (Objects.equals(stockBillLog.getStockBillType(), StockBillType.PURCHASE_STORAGE.value())) {
            // 采购入库操作
            stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
            for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
                // 更新商品与sku的库存
                this.updateProdStock(stockBillLog, addPointSkus, stockBillLogItem, stockPointSkuVOMap, changeStockList);
                sku = skuService.getOne(Wrappers.lambdaQuery(Sku.class)
                        .eq(Sku::getSkuId, stockBillLogItem.getSkuId())
                );
                stockBillLogItem.setAfterStock(sku.getStocks());
                stockBillLogItem.setPrice(sku.getPrice());
            }
        } else {
            // 其他操作
            for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
                // 普通商品出入库操作
                if (Objects.equals(stockBillLog.getStatus(), StockBillStatus.SUCCESS.value())) {
                    // 更新商品与sku的库存
                    this.updateProdStock(stockBillLog, addPointSkus, stockBillLogItem, stockPointSkuVOMap, changeStockList);
                }
                sku = skuService.getOne(Wrappers.lambdaQuery(Sku.class)
                        .eq(Sku::getSkuId, stockBillLogItem.getSkuId())
                );
                // 销售价格
                stockBillLogItem.setPrice(sku.getPrice());
                stockBillLogItem.setAfterStock(sku.getStocks());
                // 设置默认单价
                stockBillLogItem.setUnitPrice(sku.getPrice());
                // 累计总金额
                totalAmount = Arith.add(totalAmount, Arith.mul(stockBillLogItem.getStockCount(), sku.getPrice()));
                // 累计总数量
                totalCount += stockBillLogItem.getStockCount();
            }
            stockBillLog.setTotalCount(totalCount);
            stockBillLog.setTotalAmount(totalAmount);
        }
        stockPointSkuService.batchSave(addPointSkus);
        if (CollUtil.isNotEmpty(changeStockList)) {
            stockManager.skuStock(changeStockList, null);
            // 判断处理一下非自提商品的门店库存
            checkStockStatus(stockBillLog, changeStockList);
            // 库存扣减，若扣减为0，非默认仓库需删除关联数据
            if (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK)) {
                List<Long> skuIds = changeStockList.stream().map(SkuStockVO::getSkuId).collect(Collectors.toList());
                stockPointSkuService.updateStockPointSkuRelate(Collections.singletonList(stockBillLog.getStockPointId()), skuIds);
            }
        }
    }

    /**
     * 根据类型生成出入库单号
     *
     * @param stockBillType
     * @return
     */
    private String getStockBillNoByType(StockBillType stockBillType, StockType stockType) {
        if (Objects.nonNull(stockBillType)) {
            switch (stockBillType) {
                case PURCHASE_STORAGE:
                case RETURN_STORAGE:
                case OTHER_STORAGE:
                case INITIALIZE:
                case ORDER_CANCEL:
                case EDIT_STORAGE:
                case PROFIT_STORAGE:
                    // 入库单号
                    return "IB" + segmentService.getSegmentId(SegmentIdKey.STOCK);
                case PURCHASE_OUTBOUND:
                case EDIT_OUTBOUND:
                case LOSS_OUTBOUND:
                case OTHER_OUTBOUND:
                    // 出库单号
                    return "OB" + segmentService.getSegmentId(SegmentIdKey.STOCK);
                default:
                    break;
            }
        }
        if (Objects.nonNull(stockType)) {
            switch (stockType) {
                case OUT_OF_STOCK:
                    // 出库单号
                    return "OB" + segmentService.getSegmentId(SegmentIdKey.STOCK);
                case WAREHOUSING:
                    // 入库单号
                    return "IB" + segmentService.getSegmentId(SegmentIdKey.STOCK);
                default:
                    break;
            }
        }
        return null;
    }

    /**
     * 更新商品库存
     *
     * @param stockBillLog
     * @param addPointSkus
     * @param stockBillLogItem
     * @param stockPointSkuVOMap
     * @param changeStockList
     */
    private void updateProdStock(StockBillLog stockBillLog, List<StockPointSkuDTO> addPointSkus, StockBillLogItem stockBillLogItem,
                                 Map<Long, StockPointSkuVO> stockPointSkuVOMap, List<SkuStockVO> changeStockList) {
        Long prodId = stockBillLogItem.getProdId();
        Long skuId = stockBillLogItem.getSkuId();
        boolean isOther = (Objects.equals(stockBillLog.getStockBillType(), StockBillType.OTHER_STORAGE.value()) || Objects.equals(stockBillLog.getStockBillType(), StockBillType.OTHER_OUTBOUND.value())) && Objects.equals(stockBillLog.getStatus(), StockBillStatus.SUCCESS.value());
        int stock = 0;
        if (isOther) {
            // 更新商品与sku的库存
            stock = Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? -stockBillLogItem.getStockCount() : stockBillLogItem.getStockCount();
            SkuStockVO skuStockVO = new SkuStockVO(stockBillLogItem.getSkuId(), stock, stockBillLog.getStockPointId(), stockBillLog.getStockPointType());
            skuStockVO.setProdId(stockBillLogItem.getProdId());
            skuStockVO.setStatus(1);
            // 判断该库存点是否有改商品数据
            if (stockPointSkuVOMap.containsKey(stockBillLogItem.getSkuId())) {
                // 有就修改
                if (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value())) {
                    skuStockVO.setOperateType(LuaOperateEnum.SKU_SUB.value());
                } else {
                    skuStockVO.setOperateType(LuaOperateEnum.SKU_ADD.value());
                }
            } else {
                if (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value())) {
                    throw new YamiShopBindException("该库存点没有商品" + stockBillLogItem.getProdName() + "的库存");
                }
                // 没有就添加
                StockPointSkuDTO stockPointSkuDTO = new StockPointSkuDTO(
                        stockBillLog.getStockPointId(), stockBillLog.getStockPointType(),
                        stockBillLogItem.getProdId(), stockBillLogItem.getSkuId(),
                        1, stockBillLogItem.getWarehouseType(), stockBillLogItem.getWarehouseType() == null ? StockModeEnum.SINGLE_STOCK.value() : null);
                addPointSkus.add(stockPointSkuDTO);
                skuStockVO.setOperateType(LuaOperateEnum.SKU_INSERT.value());
            }
            changeStockList.add(skuStockVO);
        }
        productService.removeProdCacheByProdId(prodId);
        skuService.removeSkuCacheBySkuId(skuId, prodId);
    }

    private void checkStockStatus(StockBillLog stockBillLog, List<SkuStockVO> changeStockList) {
        // 判断一下是否是非自提的商品添加门店库存
        if (Objects.equals(stockBillLog.getStockPointType(), StockPointType.STATION.getValue()) && Objects.equals(stockBillLog.getType(), StockType.WAREHOUSING.value())) {
            List<Long> spuIds = changeStockList.stream().map(SkuStockVO::getProdId).collect(Collectors.toList());
            List<Product> productList = productService.listByIds(spuIds);
            Map<Long, String> spuModeMap = productList.stream().collect(Collectors.toMap(Product::getProdId, Product::getDeliveryMode));
            List<SkuStockVO> upStatusList = new ArrayList<>();
            for (SkuStockVO skuStockVO : changeStockList) {
                Product.DeliveryModeVO deliveryMode = Json.parseObject(spuModeMap.get(skuStockVO.getProdId()), Product.DeliveryModeVO.class);
                Boolean hasUserPickUp = deliveryMode.getHasUserPickUp();
                if (!hasUserPickUp) {
                    skuStockVO.setStatus(0);
                    skuStockVO.setOperateType(LuaOperateEnum.SKU_POINT_STATUS.value());
                    upStatusList.add(skuStockVO);
                }
            }
            if (CollUtil.isNotEmpty(upStatusList)) {
                // 门店库存更新一下状态
                String updateStock = stockManager.skuStock(upStatusList, null);
                if (!updateStock.contains(Constant.UNDERLINE)) {
                    throw new YamiShopBindException("yami.sku.stock.change");
                }
            }
        }
    }

}
